//
// Created by tang on 2020/6/18.
//

#include <cstring>
#include "ssd_1306_128_64.hpp"

template<typename T>
static inline void bounded(T &value, const T min, const T max) {
    value = value > max ? max : value < min ? min : value;
}

SSD1306_128_64::SSD1306_128_64(IIC &&iic) : wire_(iic) {

}

void SSD1306_128_64::init() {
    static const char init_cmd_list[] = {
            0xAE, // 关闭显示
            0xD5, // 设置时钟分频因子，震荡频率
            0x80, // [3:0]分频因子，[7:4]震荡频率
            0xA8, // 设置驱动路数
            0x3F, // 64行
            0xD3, // 设置显示偏移
            0x00, // 无偏移
            0x40, // 设置显示开始行数,[5:0]行数,这里为0
            0x8D, // 电荷泵设置
            0x14, // bit2 开启/关闭
            0x20, // 设置内存地址模式
            0x00, // [1:0] 00:列地址, 01:行地址, 10:页地址; 默认 10
            0xA1, // 段重定义设置, bit0:0->0=0,1->0=127
            0xC8, // 设置COM扫描方向
            0xDA, // 设置COM引脚配置
            0x12, // [5:4]配置
            0x81, // 设置对比度
            0xFF, // 最大对比度
            0xD9, // 设置预置充电周期
            0xF1, // [3:0] PHASE 1, [7:4] PHASE 2
            0xDB, // 设置VCOMH电压倍率
            0x60, //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
            0xA4, // 全局显示开启,bit0: 0->开, 1->关 (白屏/黑屏)
            0xA6, // 设置显示方式,bit0: 0->正常, 1->反色
            0x2E, // 关闭滚动
            0xAF, // 开显示

            // 重置内存位置
            0x22, 0x00, 0x07,
            0x21, 0x00, 0x7F,
    };

    for (auto cmd : init_cmd_list)
        onCmd(cmd);
}

void SSD1306_128_64::onCmd(char cmd) {
    char cmd_buff[] = {0x00, cmd};
    wire_.write(cmd_buff, sizeof(cmd_buff));
}

void SSD1306_128_64::inverse(bool on) {
    onCmd(on ? 0xA6 : 0xA7);
}

void SSD1306_128_64::clear() {
    fill(false);
}

void SSD1306_128_64::display() {
    sync();
}

void SSD1306_128_64::sync() {
    char tmp[sizeof(buffer_) + 1];
    tmp[0] = 0x40;
    memcpy(tmp + 1, buffer_, sizeof(buffer_));
    wire_.write(tmp, sizeof(tmp));
}

void SSD1306_128_64::drawPoint(Point2D p, bool bounded) {
    // bound check
    if (bounded) {
        ::bounded(p.x, 0, 127);
        ::bounded(p.y, 0, 63);
    } else {
        if (0>p.x || 0>p.y || 127 < p.x || 63 < p.y)
            return;
    }

    const unsigned byte_index = p.y / 8 * 128 + p.x;
    const unsigned bit_index = p.y % 8;
    buffer_[byte_index] |= (1U << bit_index);
}

void SSD1306_128_64::fill(bool white) {
    memset(buffer_, white ? 0xFF : 0x00, sizeof(buffer_));
}

char *SSD1306_128_64::buffer() {
    return buffer_;
}
